﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Data.SqlTypes;
using Djs.Neuronic.Core;
using Djs.Neuronic.Brain;
using Djs.Neuronic.Medium;

namespace Djs.Neuronic.Brain
{
    #region DatabaseCls : Přístup k datům databáze
    /// <summary>
    /// DatabaseCls : Přístup k datům databáze
    /// </summary>
    public partial class DatabaseCls : CommonCls
    {
        #region Konstrukce
        protected override void Init()
        {
            this.ConnectPool = new List<DatabaseConnect>();
        }
        private List<DatabaseConnect> ConnectPool;
        #endregion
        #region Zkratky na DataCommand a na Reader
        /// <summary>
        /// Vrátí databázový příkaz, vytvořený nad standardní Connection
        /// </summary>
        /// <param name="commandText"></param>
        /// <returns></returns>
        public DataCommand CreateCommand(string commandText)
        {
            return this.GetConnection().CreateCommand(commandText);
        }
        /// <summary>
        /// Vrátí databázový příkaz, vytvořený nad standardní Connection
        /// </summary>
        /// <param name="commandText"></param>
        /// <returns></returns>
        public DataCommand CreateCommand(string commandText, Int32? timeOut)
        {
            return this.GetConnection().CreateCommand(commandText, timeOut);
        }
        #endregion
        #region Přímé SQL metody : ExecuteCommand, ExecuteScript, ReadValue
        public void ExecuteCommand(string commandText)
        {
            ExecuteCommand(commandText, this.GetConnection());
        }
        public void ExecuteCommand(string commandText, Int32? timeOut)
        {
            ExecuteCommand(commandText, this.GetConnection());
        }
        public static void ExecuteCommand(string commandText, DatabaseConnect connect)
        {
            ExecuteCommand(commandText, null, connect);
        }
        public static void ExecuteCommand(string commandText, Int32? timeOut, DatabaseConnect connect)
        {
            connect.ExecuteCommand(commandText, timeOut);
        }

        public void ExecuteScript(string sqlScript)
        {
            ExecuteScript(sqlScript, this.GetConnection());
        }
        public void ExecuteScript(string sqlScript, Int32? timeOut)
        {
            ExecuteScript(sqlScript, this.GetConnection());
        }
        public static void ExecuteScript(string sqlScript, DatabaseConnect connect)
        {
            ExecuteScript(sqlScript, null, connect);
        }
        public static void ExecuteScript(string sqlScript, Int32? timeOut, DatabaseConnect connect)
        {
            connect.ExecuteScript(sqlScript, timeOut);
        }
        /// <summary>
        /// Vrátí hodnotu z prvního sloupce prvního řádku, který načte daný SQL příkaz.
        /// Pokud by SQL příkaz vracel více záznamů, další se ani nebudou načítat.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="commandText">SQL příkaz</param>
        /// <returns></returns>
        public T ReadValue<T>(string commandText)
        {
            return ReadValue<T>(commandText, this.GetConnection());
        }
        /// <summary>
        /// Vrátí první záznam, který načte daný SQL příkaz.
        /// Pokud by SQL příkaz vracel více záznamů, další se ani nebudu načítat.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="commandText">SQL příkaz</param>
        /// <param name="connect">SQL konektor</param>
        /// <returns></returns>
        public static T ReadValue<T>(string commandText, DatabaseConnect connect)
        {
            T result = default(T);

            using (DataCommand command = connect.CreateCommand(commandText))
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    if (reader.FieldCount > 0)
                        result = GetReaderValueOfType<T>(reader, 0);
                    break;
                }
                reader.Close();
            }

            return result;
        }
        #endregion
        #region ReadRecord
        /// <summary>
        /// Vrátí první záznam, který načte daný SQL příkaz.
        /// Pokud by SQL příkaz vracel více záznamů, další se ani nebudu načítat.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="commandText">SQL příkaz</param>
        /// <returns></returns>
        public T ReadRecord<T>(string commandText) where T : IRecordFill, new()
        {
            return ReadRecord<T>(commandText, this.GetConnection());
        }
        /// <summary>
        /// Vrátí první záznam, který načte daný SQL příkaz.
        /// Pokud by SQL příkaz vracel více záznamů, další se ani nebudu načítat.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="commandText">SQL příkaz</param>
        /// <param name="connect">SQL konektor</param>
        /// <returns></returns>
        public static T ReadRecord<T>(string commandText, DatabaseConnect connect) where T : IRecordFill, new()
        {
            T result = default(T);
            bool isCreated = false;

            using (DataCommand command = connect.CreateCommand(commandText))
            using (var reader = command.ExecuteReader())
            {
                RecordFillInfo data = null;
                while (reader.Read())
                {
                    if (data == null)
                        data = RecordFillInfo.Prepare(reader);

                    result = _CreateRecord<T>(data);
                    isCreated = true;
                    break;
                }
                reader.Close();
            }

            if (isCreated && typeof(T).ImplementInterface(typeof(IRecord)))
                ReadEntriesTo(result as IRecord, connect);

            return result;
        }
        /// <summary>
        /// Generická metoda pro vytvoření objektu dané třídy z dat načtených z databáze
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        private static T _CreateRecord<T>(RecordFillInfo data) where T : IRecordFill, new()
        {
            T record = new T();
            record.Fill(data);
            return record;
        }
        /// <summary>
        /// Negenerická metoda pro vytvoření objektu dané třídy z dat načtených z databáze
        /// </summary>
        /// <param name="classInfo"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private static IRecord _CreateRecord(ClassInfo classInfo, RecordFillInfo data)
        {
            IRecord record = classInfo.CreateRecord();
            if (record != null)
                record.Fill(data);
            return record;
        }
        #endregion
        #region ReadList
        /// <summary>
        /// Načte a vrátí seznam záznamů daného typu, které vrátí daný SQL příkaz
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="commandText">SQL příkaz</param>
        /// <returns></returns>
        public List<T> ReadList<T>(string commandText) where T : IRecordFill, new()
        {
            return ReadList<T>(commandText, this.GetConnection());
        }
        /// <summary>
        /// Načte a vrátí seznam záznamů daného typu, které vrátí daný SQL příkaz
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="commandText">SQL příkaz</param>
        /// <param name="connect">SQL konektor</param>
        /// <returns></returns>
        public static List<T> ReadList<T>(string commandText, DatabaseConnect connect) where T : IRecordFill, new()
        {
            List<T> result = new List<T>();

            using (DataCommand command = connect.CreateCommand(commandText))
            using (var reader = command.ExecuteReader())
            {
                RecordFillInfo data = null;
                while (reader.Read())
                {
                    if (data == null)
                        data = RecordFillInfo.Prepare(reader);

                    result.Add(_CreateRecord<T>(data));
                }
                reader.Close();
            }

            if (result.Count > 0 && typeof(T).ImplementInterface(typeof(IRecord)))
                ReadEntriesTo(result.OfType<IRecord>(), connect);

            return result;
        }
        #endregion
        #region Načítání položek do záznamů Master. Je primárně řešeno pro hromadné načítání pro soupis Master záznamů, ale dovoluje načítat položky i pro jeden záznam a pro jeden druh položek (u master tříd s více položkami)
        /// <summary>
        /// Zajistí načtení všech položek do daného objektu IRecord
        /// </summary>
        /// <param name="record"></param>
        public void ReadEntriesTo(IRecord record)
        { _ReadEntriesTo(new IRecord[] { record }, null, this.GetConnection()); }
        /// <summary>
        /// Zajistí načtení všech položek do daného soupisu objektů IRecord
        /// </summary>
        /// <param name="records"></param>
        public void ReadEntriesTo(IEnumerable<IRecord> records)
        { _ReadEntriesTo(records, null, this.GetConnection()); }
        /// <summary>
        /// Zajistí načtení položek daného jména do daného objektu IRecord
        /// </summary>
        /// <param name="record"></param>
        /// <param name="name"></param>
        public void ReadEntriesTo(IRecord record, Type entriesType)
        { _ReadEntriesTo(new IRecord[] { record }, entriesType, this.GetConnection()); }
        /// <summary>
        /// Zajistí načtení položek daného jména do daného soupisu objektů IRecord
        /// </summary>
        /// <param name="records"></param>
        /// <param name="name"></param>
        public void ReadEntriesTo(IEnumerable<IRecord> records, Type entriesType)
        { _ReadEntriesTo(records, entriesType, this.GetConnection()); }
        /// <summary>
        /// Zajistí načtení všech položek do daného objektu IRecord
        /// </summary>
        /// <param name="record"></param>
        /// <param name="connect"></param>
        public static void ReadEntriesTo(IRecord record, DatabaseConnect connect)
        { _ReadEntriesTo(new IRecord[] { record }, null, connect); }
        /// <summary>
        /// Zajistí načtení všech položek do daného soupisu objektů IRecord
        /// </summary>
        /// <param name="records"></param>
        /// <param name="connect"></param>
        public static void ReadEntriesTo(IEnumerable<IRecord> records, DatabaseConnect connect)
        { _ReadEntriesTo(records, null, connect); }
        /// <summary>
        /// Zajistí načtení položek daného jména do daného objektu IRecord
        /// </summary>
        /// <param name="record"></param>
        /// <param name="entriesType"></param>
        /// <param name="connect"></param>
        public static void ReadEntriesTo(IRecord record, Type entriesType, DatabaseConnect connect)
        { _ReadEntriesTo(new IRecord[] { record }, entriesType, connect); }
        /// <summary>
        /// Zajistí načtení položek daného jména do daného soupisu objektů IRecord
        /// </summary>
        /// <param name="records"></param>
        /// <param name="entriesType"></param>
        /// <param name="connect"></param>
        public static void ReadEntriesTo(IEnumerable<IRecord> records, Type entriesType, DatabaseConnect connect)
        { _ReadEntriesTo(records, entriesType, connect); }
        /// <summary>
        /// Zajistí načtení položek daného jména nebo všech do daných objektů IRecord
        /// </summary>
        /// <param name="records"></param>
        /// <param name="entriesType"></param>
        /// <param name="connect"></param>
        private static void _ReadEntriesTo(IEnumerable<IRecord> records, Type entriesType, DatabaseConnect connect)
        {
            // Vstupní záznamy si rozčlením podle jejich reálného typu (případné NULL hodím stranou), a zpracuji je striktně typově:
            IEnumerable<IGrouping<Type, IRecord>> groups = records.GroupBy<IRecord, Type>(r => (r == null ? (Type)null : r.GetType()));
            foreach (IGrouping<Type, IRecord> group in groups)
                _ReadEntriesTo(group.Key, group, entriesType, connect);
        }
        /// <summary>
        /// Zajistí načtení položek daného jména nebo všech do daných objektů IRecord.
        /// Vstupující záznamy (records) jsou všechny jedné třídy, a to (masterType).
        /// </summary>
        /// <param name="masterType">Typ master záznamu</param>
        /// <param name="records">Soupis fyzických master záznamů. Nikdy není prázdný (obsahuje alespoň jednu položku), nikdy neobsahuje null hodnotu.</param>
        /// <param name="entriesType">Požadavek na načítání položek jen tohoto typu. Null = načte všechy seznamy položek dané Master třídy podle deklarace.</param>
        /// <param name="connect"></param>
        private static void _ReadEntriesTo(Type masterType, IEnumerable<IRecord> records, Type entriesType, DatabaseConnect connect)
        {
            ClassInfo masterClassInfo = Stator.Declarations.ClassDeclaration.TryGetClassInfo(masterType);
            if (masterClassInfo == null) return;              // Daná datová třída nemá deklaraci.

            List<EntryInfo> entryInfoList = masterClassInfo.EntryList.Where(ent => (entriesType == null || ent.EntryType == entriesType)).ToList();
            if (entryInfoList.Count > 0)
            {
                Dictionary<int, IRecord> recordDict = records.ToDictionary(r => r.Id, true);
                foreach (EntryInfo entryInfo in entryInfoList)
                    _ReadEntriesTo(masterClassInfo, recordDict, entryInfo, connect);
            }
        }
        /// <summary>
        /// Zajistí načtení určených položek do daných objektů IRecord.
        /// Pro typ záznamu (masterType), pro dictionary master záznamů (recordDict), kde indexem je ID a hodnotou je konkrétní master záznam (IRecord)
        /// načte položky definované daty v (entryInfo), z dodané connection.
        /// </summary>
        /// <param name="masterType"></param>
        /// <param name="recordDict"></param>
        /// <param name="entryInfo"></param>
        /// <param name="connect"></param>
        private static void _ReadEntriesTo(ClassInfo masterClassInfo, Dictionary<int, IRecord> recordDict, EntryInfo entryInfo, DatabaseConnect connect)
        {
            List<string> commandTexts = entryInfo.EntryClass.GetSqlSelects(recordDict.Keys);  // SQL SELECTy pro načtení záznamů POLOŽEK (entryInfo.EntryClass), s filtrem na ID záznamů Master (recordDict.Keys)
            foreach (string commandText in commandTexts)
            {   // SQL selecty rozdělené do 16KB textu:
                List<IRecord> entries = new List<IRecord>();

                // Načteme položky:
                using (DataCommand command = connect.CreateCommand(commandText))
                using (var reader = command.ExecuteReader())
                {
                    RecordFillInfo data = null;
                    while (reader.Read())
                    {
                        if (data == null)
                            data = RecordFillInfo.Prepare(reader);

                        IRecord record = _CreateRecord(entryInfo.EntryClass, data);
                        if (record != null)
                            entries.Add(record);
                    }
                    reader.Close();
                }

                // Položky sgrupujeme podle čísla ID master záznamu (to je uvedeno v jejich ID) a po grupách vložíme do Master záznamů:
                IEnumerable<IGrouping<int, IRecord>> entriesGroups = entries.GroupBy(r => r.Id);
                foreach (IGrouping<int, IRecord> entriesGroup in entriesGroups)
                {
                    IRecord masterRecord;
                    if (recordDict.TryGetValue(entriesGroup.Key, out masterRecord))
                    {
                        IEntries iEntries = entryInfo.GetIEntries(masterRecord);
                        if (iEntries != null)
                            iEntries.EntriesLoaded = entriesGroup;
                    }
                }
            }
        }
        #endregion
        #region Static TypeConvertor method
        /// <summary>
        /// Vrátí defaultní hodnotu daného typu
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object GetDefaultValueOfType(Type type)
        {
            return GetDefaultValueOfType(type.Name);
        }
        /// <summary>
        /// Vrátí defaultní hodnotu daného typu
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static object GetDefaultValueOfType(string typeName)
        {
            switch (typeName)
            {
                case "Boolean": return false;
                case "Byte": return (Byte)0;
                case "Bytes": return null;
                case "DateTime": return DateTime.MinValue;
                case "DateTimeOffset": return DateTimeOffset.MinValue;
                case "Decimal": return (Decimal)0m;
                case "Double": return (Double)0;
                case "Float": return (float)0;
                case "Guid": return Guid.Empty;
                case "Char": return '\0';
                case "Chars": return null;
                case "Int16": return (Int16)0;
                case "Int32": return (Int32)0;
                case "Int64": return (Int64)0;
                case "SqlBinary": return System.Data.SqlTypes.SqlBinary.Null;
                case "SqlBoolean": return System.Data.SqlTypes.SqlBoolean.Null;
                case "SqlByte": return System.Data.SqlTypes.SqlByte.Null;
                case "SqlBytes": return System.Data.SqlTypes.SqlBytes.Null;
                case "SqlDateTime": return System.Data.SqlTypes.SqlDateTime.Null;
                case "SqlDecimal": return System.Data.SqlTypes.SqlDecimal.Null;
                case "SqlDouble": return System.Data.SqlTypes.SqlDouble.Null;
                case "SqlGuid": return System.Data.SqlTypes.SqlGuid.Null;
                case "SqlChars": return System.Data.SqlTypes.SqlChars.Null;
                case "SqlInt16": return System.Data.SqlTypes.SqlInt16.Null;
                case "SqlInt32": return System.Data.SqlTypes.SqlInt32.Null;
                case "SqlInt64": return System.Data.SqlTypes.SqlInt64.Null;
                case "SqlMoney": return System.Data.SqlTypes.SqlMoney.Null;
                case "SqlSingle": return System.Data.SqlTypes.SqlSingle.Null;
                case "SqlString": return System.Data.SqlTypes.SqlString.Null;
                case "String": return (String)null;
                case "TimeSpan": return TimeSpan.Zero;
                case "Object": return null;
            }
            return null;
        }
        /// <summary>
        /// Vrátí korektní typovou hodnotu z readeru z daného sloupce.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <param name="columnIndex"></param>
        /// <returns></returns>
        public static T GetReaderValueOfType<T>(SqlDataReader reader, int columnIndex)
        {
            Type type = typeof(T);
            object defaultValue = GetDefaultValueOfType(type);
            object value = GetReaderValueOfType(reader, columnIndex, type.Name, defaultValue);
            return (T)value;
        }
        /// <summary>
        /// Vrátí hodnotu z readeru daného typu.
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="columnIndex"></param>
        /// <param name="dataTypeName"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static object GetReaderValueOfType(SqlDataReader reader, int columnIndex, string dataTypeName, object defaultValue)
        {
            if (reader.IsDBNull(columnIndex)) return (defaultValue);
            switch (dataTypeName)
            {
                case "Boolean": return reader.GetBoolean(columnIndex);
                case "Byte": return reader.GetByte(columnIndex);
                case "Bytes": return GetBytes(reader, columnIndex);
                case "DateTime": return reader.GetDateTime(columnIndex);
                case "DateTimeOffset": return reader.GetDateTimeOffset(columnIndex);
                case "Decimal": return reader.GetDecimal(columnIndex);
                case "Double": return reader.GetDouble(columnIndex);
                case "Float": return reader.GetFloat(columnIndex);
                case "Guid": return reader.GetGuid(columnIndex);
                case "Char": return reader.GetChar(columnIndex);
                case "Chars": return GetChars(reader, columnIndex);
                case "Int16": return reader.GetInt16(columnIndex);
                case "Int32": return reader.GetInt32(columnIndex);
                case "Int64": return reader.GetInt64(columnIndex);
                case "SqlBinary": return reader.GetSqlBinary(columnIndex);
                case "SqlBoolean": return reader.GetSqlBoolean(columnIndex);
                case "SqlByte": return reader.GetSqlByte(columnIndex);
                case "SqlBytes": return reader.GetSqlBytes(columnIndex);
                case "SqlDateTime": return reader.GetSqlDateTime(columnIndex);
                case "SqlDecimal": return reader.GetSqlDecimal(columnIndex);
                case "SqlDouble": return reader.GetSqlDouble(columnIndex);
                case "SqlGuid": return reader.GetSqlGuid(columnIndex);
                case "SqlChars": return reader.GetSqlChars(columnIndex);
                case "SqlInt16": return reader.GetSqlInt16(columnIndex);
                case "SqlInt32": return reader.GetSqlInt32(columnIndex);
                case "SqlInt64": return reader.GetSqlInt64(columnIndex);
                case "SqlMoney": return reader.GetSqlMoney(columnIndex);
                case "SqlSingle": return reader.GetSqlSingle(columnIndex);
                case "SqlString": return reader.GetSqlString(columnIndex);
                case "String": return reader.GetString(columnIndex);
                case "TimeSpan": return reader.GetTimeSpan(columnIndex);
                case "Object": return reader.GetValue(columnIndex);
            }
            return reader.GetProviderSpecificValue(columnIndex);
        }
        private static object GetBytes(SqlDataReader reader, int columnIndex)
        {
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                int bufferLen = 0x10000;
                byte[] buffer = new byte[bufferLen];
                while (true)
                {
                    long readLen = reader.GetBytes(columnIndex, 0L, buffer, 0, bufferLen);
                    if (readLen == 0)
                        return ms.ToArray();
                    ms.Write(buffer, 0, (int)readLen);
                }
            }
        }
        private static object GetChars(SqlDataReader reader, int columnIndex)
        {
            throw new NotImplementedException();
        }
        #endregion
        #region Instanční connectory
        /// <summary>
        /// Výchozí connection string
        /// </summary>
        public string ConnectString { get; set; }
        /// <summary>
        /// Vrátí první volnou connection
        /// </summary>
        /// <returns></returns>
        public DatabaseConnect GetConnection()
        {
            string connectString = this.ConnectString;
            if (String.IsNullOrEmpty(connectString))
                throw new NeuroSysException("Connect is not defined.");

            DatabaseConnect connect = this.ConnectPool.FirstOrDefault(dc => !dc.Busy && dc.Applicable);
            if (connect == null)
            {
                if (this.ConnectPool.Count > 7)
                    throw new NeuroSysException("Connect pool is full, can not create another.");

                connect = new DatabaseConnect(connectString);
                this.ConnectPool.Add(connect);
            }
            return connect;
        }
        #endregion
        #region Static connectory
        public static SqlConnection CreateConnection(string connect)
        {
            return CreateConnection(new SqlConnectionStringBuilder(connect));
        }
        public static SqlConnection CreateConnection(SqlConnectionStringBuilder connect)
        {
            SqlConnection connection = new SqlConnection(connect.ConnectionString);
            connection.Open();
            return connection;
        }
        #endregion
        #region Static services
        /// <summary>
        /// Vrátí sadu SQL příkazů vytvořených z dodaného vzorce (sqlCommand), 
        /// do něhož vloží na specifikované místo (sqlToken) naformátované hodnoty (items) uzavřené v závorce a oddělené čárkou.
        /// Konkrétně: pro sqlCommand = "SELECT * FROM tabulka WHERE id in {{SqlToken}}", pro sqlToken = "{{SqlToken}}"
        /// a pro hodnoty: { 124,1050,1255,1368,21501} 
        /// vrátí select "SELECT * FROM tabulka WHERE id in (124, 1050, 1255, 1368, 21501)".
        /// Pokud by select byl delší než 16KiB, rozdělí je do sady textů.
        /// </summary>
        /// <param name="sqlCommand"></param>
        /// <param name="sqlToken"></param>
        /// <param name="items"></param>
        /// <returns></returns>
        public static List<string> CreateSqlBatches(string sqlCommand, string sqlToken, System.Collections.IEnumerable items)
        {
            return CreateSqlBatches(sqlCommand, sqlToken, items, "(", ", ", ")", 16386);
        }
        /// <summary>
        /// Vrátí sadu SQL příkazů vytvořených z dodaného vzorce (sqlCommand), 
        /// do něhož vloží na specifikované místo (sqlToken) naformátované hodnoty (items) uzavřené v závorce a oddělené čárkou.
        /// Konkrétně: pro sqlCommand = "SELECT * FROM tabulka WHERE id in {{SqlToken}}", pro sqlToken = "{{SqlToken}}"
        /// a pro hodnoty: { 124,1050,1255,1368,21501} 
        /// vrátí select "SELECT * FROM tabulka WHERE id in (124, 1050, 1255, 1368, 21501)".
        /// Pokud by select byl delší než maxLength, rozdělí je do sady textů.
        /// </summary>
        /// <param name="sqlCommand"></param>
        /// <param name="sqlToken"></param>
        /// <param name="items"></param>
        /// <param name="maxLength"></param>
        /// <returns></returns>
        public static List<string> CreateSqlBatches(string sqlCommand, string sqlToken, System.Collections.IEnumerable items, int maxLength)
        {
            return CreateSqlBatches(sqlCommand, sqlToken, items, "(", ", ", ")", maxLength);
        }
        /// <summary>
        /// Vrátí sadu SQL příkazů vytvořených z dodaného vzorce (sqlCommand), 
        /// do něhož vloží na specifikované místo (sqlToken) naformátované hodnoty (items) uzavřené v závorce a oddělené čárkou.
        /// Konkrétně: pro sqlCommand = "SELECT * FROM tabulka WHERE id in {{SqlToken}}", pro sqlToken = "{{SqlToken}}"
        /// a pro hodnoty: { 124,1050,1255,1368,21501} 
        /// vrátí select "SELECT * FROM tabulka WHERE id in (124, 1050, 1255, 1368, 21501)".
        /// Pokud by select byl delší než maxLength, rozdělí je do sady textů.
        /// </summary>
        /// <param name="sqlCommand"></param>
        /// <param name="sqlToken"></param>
        /// <param name="items"></param>
        /// <param name="maxLength"></param>
        /// <returns></returns>
        public static List<string> CreateSqlBatches(string sqlCommand, string sqlToken, System.Collections.IEnumerable items, 
            string delimiterBefore, string delimiterInner, string delimiterAfter, int maxLength)
        {
            List<string> result = new List<string>();
            StringBuilder sb = new StringBuilder();
            int sqlCommandLength = sqlCommand.Length - sqlToken.Length;
            foreach (object item in items)
            {
                string formattedItem = delimiterInner + SqlFormat.ToSql(item);
                int sbLength = sb.Length;
                if (sbLength > 0 && (sqlCommandLength + sbLength + formattedItem.Length) > maxLength)
                    CreateSqlBatchesOne(sqlCommand, sqlToken, sb, delimiterBefore, delimiterInner, delimiterAfter, result);
                sb.Append(formattedItem);
            }
            if (sb.Length > 0)
                CreateSqlBatchesOne(sqlCommand, sqlToken, sb, delimiterBefore, delimiterInner, delimiterAfter, result);
            
            return result;
        }
        private static void CreateSqlBatchesOne(string sqlCommand, string sqlToken, StringBuilder sb, string delimiterBefore, string delimiterInner, string delimiterAfter, List<string> result)
        {
            string sqlExp = delimiterBefore + sb.ToString().Substring(delimiterInner.Length) + delimiterAfter;
            string sqlCmd = sqlCommand.Replace(sqlToken, sqlExp);
            result.Add(sqlCmd);
            sb.Clear();
        }
        /// <summary>
        /// Rozdělí daný text na části, oddělené textem GO na samostatném řádku.
        /// Jde o jednotlivé SQL příkazy. Příkaz GO není příkaz SQL, ale metapříkaz skriptu.
        /// </summary>
        /// <param name="sqlScript"></param>
        /// <returns></returns>
        public static List<string> CreateSqlScripts(string sqlScript)
        {
            string separator1 = Environment.NewLine + "GO" + Environment.NewLine;
            string separator2 = Environment.NewLine + "go" + Environment.NewLine;
            List<string> result = new List<string>(sqlScript.Split(new string[] { separator1, separator2 }, StringSplitOptions.None));
            return result;
        }
        #endregion
        #region Kontrola connect stringu načteného z konfigurace, vyhledání serveru a databáze, automatické patchování databáze
        /// <summary>
        /// Metoda prověří daný connectstring, zda je korektní (zadaný), zda daný server a databáze existuje, 
        /// a zda databáze obsahuje správná data. 
        /// Ověří i správnost struktury dat, v případě potřeby provede patchování (po dialogu nebo automaticky).
        /// Používá dialog podle předvolby showDialog.
        /// </summary>
        /// <param name="connectString"></param>
        /// <param name="showDialog"></param>
        /// <returns></returns>
        internal string CheckConnectString(string connectString, bool showDialog)
        {
            using (DatabaseManagerCls manager = new DatabaseManagerCls(this.Creator, showDialog))
            {
                return manager.CheckConnectString(connectString);
            }
        }
        #endregion
        #region Načítání informací o objektech databáze
        #region - databáze
        /// <summary>
        /// Načte a vrátí informace o databázích
        /// </summary>
        /// <returns></returns>
        public List<DbElementDatabase> DbInfoGetDatabases()
        { return _DbInfoGetDatabases(this.GetConnection(), false); }
        /// <summary>
        /// Načte a vrátí informace o databázích
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <returns></returns>
        public static List<DbElementDatabase> DbInfoGetDatabases(DatabaseConnect connect)
        { return _DbInfoGetDatabases(connect, false); }
        /// <summary>
        /// Načte a vrátí informace o databázích
        /// </summary>
        /// <param name="withSystem">Načíst i systémové databáze</param>
        /// <returns></returns>
        public List<DbElementDatabase> DbInfoGetDatabases(bool withSystem)
        { return _DbInfoGetDatabases(this.GetConnection(), withSystem); }
        /// <summary>
        /// Načte a vrátí informace o databázích
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <param name="withSystem">Načíst i systémové databáze</param>
        /// <returns></returns>
        public static List<DbElementDatabase> DbInfoGetDatabases(DatabaseConnect connect, bool withSystem)
        { return _DbInfoGetDatabases(connect, withSystem); }
        /// <summary>
        /// Načte a vrátí informace o požadované databázi
        /// </summary>
        /// <param name="databaseName">Jméno databáze</param>
        /// <returns></returns>
        public DbElementDatabase DbInfoGetDatabase(string databaseName)
        { return _DbInfoGetDatabase(this.GetConnection(), databaseName); }
        /// <summary>
        /// Načte a vrátí informace o požadované databázi
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <param name="databaseName">Jméno databáze</param>
        /// <returns></returns>
        public static DbElementDatabase DbInfoGetDatabase(DatabaseConnect connect, string databaseName)
        { return _DbInfoGetDatabase(connect, databaseName); }
        private static List<DbElementDatabase> _DbInfoGetDatabases(DatabaseConnect connect, bool withSystem)
        {
            string where = (withSystem ? "" : DbElementDatabase.SqlFilterNoSystem);
            string sql = DbElementDatabase.SqlSelect.Replace(DbElementDatabase.TOKEN_WHERE, where);
            return ReadList<DbElementDatabase>(sql, connect);
        }
        private static DbElementDatabase _DbInfoGetDatabase(DatabaseConnect connect, string databaseName)
        {
            string sql = DbElementDatabase.SqlSelectForName(databaseName);
            return ReadRecord<DbElementDatabase>(sql, connect);
        }
        #endregion
        #region - schema
        /// <summary>
        /// Vrátí všechna schemata (vlastníci objektů) aktuální databáze
        /// </summary>
        /// <returns></returns>
        public List<DbElementSchema> DbInfoGetSchemas()
        { return _DbInfoGetSchemas(this.GetConnection(), null); }
        /// <summary>
        /// Vrátí všechna schemata (vlastníci objektů) aktuální databáze
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <returns></returns>
        public static List<DbElementSchema> DbInfoGetSchemas(DatabaseConnect connect)
        { return _DbInfoGetSchemas(connect, null); }
        /// <summary>
        /// Vrátí všechna schemata (vlastníci objektů) aktuální databáze, vyhovující danému filtru
        /// </summary>
        /// <param name="where">Filtr</param>
        /// <returns></returns>
        public List<DbElementSchema> DbInfoGetSchemas(string where)
        { return _DbInfoGetSchemas(this.GetConnection(), where); }
        /// <summary>
        /// Vrátí všechna schemata (vlastníci objektů) aktuální databáze, vyhovující danému filtru
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <param name="where">Filtr</param>
        /// <returns></returns>
        public static List<DbElementSchema> DbInfoGetSchemas(DatabaseConnect connect, string where)
        { return _DbInfoGetSchemas(connect, where); }
        /// <summary>
        /// Vrátí schema (vlastník objektů) aktuální databáze, daného jména
        /// </summary>
        /// <param name="schemaName">Název schematu</param>
        /// <returns></returns>
        public DbElementSchema DbInfoGetSchema(string schemaName)
        { return _DbInfoGetSchema(this.GetConnection(), schemaName); }
        /// <summary>
        /// Vrátí schema (vlastník objektů) aktuální databáze, daného jména
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <param name="schemaName">Název schematu</param>
        /// <returns></returns>
        public static DbElementSchema DbInfoGetSchema(DatabaseConnect connect, string schemaName)
        { return _DbInfoGetSchema(connect, schemaName); }
        private static List<DbElementSchema> _DbInfoGetSchemas(DatabaseConnect connect, string where)
        {
            string sql = DbElementSchema.SqlSelect.Replace(DbElementDatabase.TOKEN_WHERE, (where == null ? "" : where));
            return ReadList<DbElementSchema>(sql, connect);
        }
        private static DbElementSchema _DbInfoGetSchema(DatabaseConnect connect, string schemaName)
        {
            string sql = DbElementSchema.SqlSelectForName(schemaName);
            return ReadRecord<DbElementSchema>(sql, connect);
        }
        #endregion
        #region - tabulky
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze
        /// </summary>
        /// <returns></returns>
        public List<DbElementTable> DbInfoGetTables()
        { return _DbInfoGetTables(this.GetConnection(), null); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <returns></returns>
        public static List<DbElementTable> DbInfoGetTables(DatabaseConnect connect)
        { return _DbInfoGetTables(connect, null); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze, vyhovující danému filtru
        /// </summary>
        /// <returns></returns>
        public List<DbElementTable> DbInfoGetTables(string where)
        { return _DbInfoGetTables(this.GetConnection(), where); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze, vyhovující danému filtru
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <returns></returns>
        public static List<DbElementTable> DbInfoGetTables(DatabaseConnect connect, string where)
        { return _DbInfoGetTables(connect, where); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze
        /// </summary>
        /// <returns></returns>
        public DbElementTable DbInfoGetTable(string tableName)
        { return _DbInfoGetTable(this.GetConnection(), tableName, false); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze, vyhovující danému filtru
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <returns></returns>
        public static DbElementTable DbInfoGetTable(DatabaseConnect connect, string tableName)
        { return _DbInfoGetTable(connect, tableName, false); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze
        /// </summary>
        /// <returns></returns>
        public DbElementTable DbInfoGetTable(string tableName, bool withContent)
        { return _DbInfoGetTable(this.GetConnection(), tableName, withContent); }
        /// <summary>
        /// Vrátí všechny tabulky z aktuální databáze, vyhovující danému filtru
        /// </summary>
        /// <param name="connect">DB konektor</param>
        /// <returns></returns>
        public static DbElementTable DbInfoGetTable(DatabaseConnect connect, string tableName, bool withContent)
        { return _DbInfoGetTable(connect, tableName, withContent); }
        private static List<DbElementTable> _DbInfoGetTables(DatabaseConnect connect, string where)
        {
            string sql = DbElementTable.SqlSelect.Replace(DbElementTable.TOKEN_WHERE, (where == null ? "" : where));
            return ReadList<DbElementTable>(sql, connect);
        }
        private static DbElementTable _DbInfoGetTable(DatabaseConnect connect, string tableName, bool withContent)
        {
            string sql = DbElementTable.SqlSelectForName(tableName);
            DbElementTable table = ReadRecord<DbElementTable>(sql, connect);
            if (table != null && withContent)
                table.LoadContent(connect);
            return table;
        }
        #endregion
        #endregion
        #region Self test
        internal override void SelfTest()
        {
            var declaration = this.Creator.Declarations.DatabaseDeclaration;

            List<DbElementDatabase> databases = DbInfoGetDatabases();

            DbElementTable neuronicTable = DbInfoGetTable("bio.neurons");
            if (neuronicTable != null)
            {
                int columnCount = neuronicTable.ColumnList.Count;
                int indexCount = neuronicTable.IndexList.Count;
            }
//            List<DbElementTable> tables = DatabaseReflector.GetTables("left(sys.objects.name,10) = 'product_or'", this.GetConnection());
        }
        #endregion
    }
    #endregion
    #region DatabaseConnect : Konektor do databáze, jedna connection
    /// <summary>
    /// DatabaseConnect : Konektor do databáze, jedna connection
    /// </summary>
    public class DatabaseConnect : IDisposable
    {
        #region Konstrukce
        public DatabaseConnect(string connectString)
        {
            this._Init(connectString, null);
        }
        public DatabaseConnect(SqlConnectionStringBuilder connection)
        {
            this._Init(connection.ConnectionString, null);
        }
        public DatabaseConnect(string connectString, string dbName)
        {
            this._Init(connectString, dbName);
        }
        public DatabaseConnect(SqlConnectionStringBuilder connection, string dbName)
        {
            this._Init(connection.ConnectionString, dbName);
        }
        #endregion
        #region IDisposable Members
        void IDisposable.Dispose()
        {
            if (this._Connector != null)
            {
                if (this._Transaction != null)
                {
                    this._Transaction.Rollback();
                    this._Transaction = null;
                }
                this._Connector.Close();
                this._Connector = null;
                this._Working = false;
                this._Applicable = null;
            }
        }
        #endregion
        #region Property, Iniciace
        public string Server { get { return this._ConnectData.DataSource; } }
        public string InitialDatabase { get { return this._ConnectInitialDatabase; } }
        public string CurrentDatabase { get { return this._ConnectCurrentDatabase; } }
        public bool IntegratedAutentication { get { return this._ConnectData.IntegratedSecurity; } }
        public string UserName { get { return this._ConnectData.UserID; } }
        public int ConnectTimeout { get { return this._ConnectData.ConnectTimeout; } }
        public string ConnectionString { get { return this._ConnectString; } }
        public void ChangeDatabase(string newDatabase)
        {
            if (!String.IsNullOrEmpty(newDatabase) && this.Ready)
            {
                this.Connector.ChangeDatabase(newDatabase);
                this._ConnectCurrentDatabase = newDatabase;
            }
        }
        private void _Init(string connectString, string dbName)
        {
            this._ConnectorLock = new object();
            this._ConnectString = connectString;
            this._ConnectData = (String.IsNullOrEmpty(connectString) ? (SqlConnectionStringBuilder)null : new SqlConnectionStringBuilder(connectString));
            if (this._ConnectData != null && dbName != null)
                this._ConnectData.InitialCatalog = dbName;
            this._ConnectInitialDatabase = (dbName != null ? dbName : (this._ConnectData != null ? this._ConnectData.InitialCatalog : null));
            this._ConnectCurrentDatabase = this._ConnectInitialDatabase;
        }
        private string _ConnectString;
        private SqlConnectionStringBuilder _ConnectData;
        private string _ConnectInitialDatabase;
        private string _ConnectCurrentDatabase;
        #endregion
        #region Stav, chyba
        /// <summary>
        /// true, pokud je v pořádku zadání (zadání existuje, má vyplněné údaje Server, Database, a je buď IntegratedAutentication nebo je zadané UserName).
        /// </summary>
        public bool Correct
        {
            get { return (this._ConnectData != null && !String.IsNullOrEmpty(this.Server) && !String.IsNullOrEmpty(this.InitialDatabase) && (this.IntegratedAutentication || !String.IsNullOrEmpty(this.UserName))); }
        }
        /// <summary>
        /// true, pokud tato connection je použitelná (=má správné zadání, nedošlo k chybě, nepracuje, a connection na server existuje, je otevřená a nevyhodila chybu).
        /// Pokud tato conection dosud nebyla připojená, pak se nyní provede Connect, aby bylo jisté že connection je v pořádku.
        /// </summary>
        public bool Ready
        {
            get
            {
                if (!this.Correct) return false;
                if (this._Applicable.HasValue && !this._Applicable.Value) return false;
                if (this._Connector == null)
                {
                    this._GetConnector();
                    if (this._Applicable.HasValue && !this._Applicable.Value) return false;
                }
                return !this._Working;
            }
        }
        /// <summary>
        /// true, pokud tato connection je teoreticky použitelná (=má správné zadání, nedošlo k chybě).
        /// Pokud tato conection je nyní v používání, vrací false.
        /// Pokud tato conection dosud nebyla připojená, pak se vrací true.
        /// </summary>
        public bool Applicable
        {
            get
            {
                if (!this.Correct) return false;
                if (this._Applicable.HasValue && !this._Applicable.Value) return false;
                if (this._Connector == null) return true;
                return !this._Working;
            }
        }
        /// <summary>
        /// true, pokud tato connection nyní fyzicky pracuje (=má správné zadání, nedošlo k chybě, právě je aktivní nějaký příkaz).
        /// Pokud tato conection dosud nebyla připojená, pak se vrací false.
        /// </summary>
        public bool Busy
        {
            get
            {
                if (!this.Correct) return false;
                if (this._Applicable.HasValue && !this._Applicable.Value) return false;
                if (this._Connector == null) return false;
                return this._Working;
            }
        }
        /// <summary>
        /// Chyba zaregistrovaná při pokusu o připojení
        /// </summary>
        public Exception ConnectException
        {
            get { return this._ConnectException; }
            set
            {
                this._ConnectException = value;
                if (value != null)
                    this._Applicable = false;
            }
        }
        private Exception _ConnectException;
        private bool? _Applicable { get; set; }
        #endregion
        #region Konektivita
        /// <summary>
        /// Konektor do databáze.
        /// Pozor, je autoinstanční. Pokud je zadání chybné, vrátí null.
        /// Pozor, get může vyhodit chybu.
        /// </summary>
        public SqlConnection Connector { get { return this._GetConnector(); } }
        private SqlConnection _GetConnector()
        {
            try
            {
                if (!this.Correct)
                    throw new NeuroSysException("Connect is not correct.");
                if (this._Connector == null)
                {
                    lock (this._ConnectorLock)
                    {
                        if (this._Connector == null)
                        {
                            this._Connector = new SqlConnection(this._ConnectData.ConnectionString);
                            this._Connector.Open();
                        }
                    }
                    this._Applicable = true;
                }
            }
            catch (Exception exc)
            {
                this.ConnectException = exc;
                this._Connector = null;
            }
            return this._Connector;
        }
        private SqlConnection _Connector;
        private object _ConnectorLock;
        private bool _Working;
        #endregion
        #region Transakčnost
        /// <summary>
        /// Aktuální transakce. Může být null.
        /// </summary>
        public SqlTransaction Transaction { get { return this._Transaction; } }
        private SqlTransaction _Transaction;
        #endregion
        #region Příkazy
        /// <summary>
        /// Vrátí databázový příkaz
        /// </summary>
        /// <param name="commandText"></param>
        /// <returns></returns>
        public DataCommand CreateCommand(string commandText)
        {
            return this.CreateCommand(commandText, null);
        }
        /// <summary>
        /// Vrátí databázový příkaz
        /// </summary>
        /// <param name="commandText"></param>
        /// <returns></returns>
        public DataCommand CreateCommand(string commandText, Int32? timeOut)
        {
            this._Working = true;
            DataCommand command = DataCommand.Create(commandText, this);
            if (timeOut.HasValue)
                command.CommandTimeout = timeOut.Value;
            return command;
        }
        /// <summary>
        /// Voláno po dokončení běhu SQL commandu
        /// </summary>
        /// <param name="e"></param>
        internal void CommandStatementCompleted(DataCommand command, StatementCompletedEventArgs e)
        {
            this._Working = false;
        }
        #endregion
        #region Spuštění příkazu, spuštění skriptu
        /// <summary>
        /// Spustí daný příkaz. Nesmí obsahovat GO, to by byl skript, na to je tu metoda ExecuteScript().
        /// </summary>
        /// <param name="commandText"></param>
        public void ExecuteCommand(string commandText)
        {
            this.ExecuteCommand(commandText, (int?)null);
        }
        /// <summary>
        /// Spustí daný příkaz. Nesmí obsahovat GO, to by byl skript, na to je tu metoda ExecuteScript().
        /// </summary>
        /// <param name="commandText"></param>
        public void ExecuteCommand(string commandText, int? timeOut)
        {
            if (String.IsNullOrEmpty(commandText))
                return;
            using (DataCommand command = this.CreateCommand(commandText, timeOut))
            {
                command.ExecuteNonQuery();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        public void ExecuteScript(string sqlScript)
        {
            this.ExecuteScript(sqlScript, (int?)null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commandText"></param>
        public void ExecuteScript(string sqlScript, int? timeOut)
        {
            List<string> commands = DatabaseCls.CreateSqlScripts(sqlScript);
            foreach (string commandText in commands)
                this.ExecuteCommand(commandText, timeOut);
        }
        #endregion
    }
    #endregion
    #region DataCommand : Příkaz k provedení proti databázi, obálka nad SqlCommand
    /// <summary>
    /// DataCommand : Příkaz k provedení proti databázi, obálka nad SqlCommand
    /// </summary>
    public class DataCommand : IDisposable
    {
        #region Konstrukce
        internal static DataCommand Create(string commandText, DatabaseConnect connect)
        {
            return new DataCommand(commandText, connect, null);
        }
        internal static DataCommand Create(string commandText, DatabaseConnect connect, Int32? timeOut)
        {
            return new DataCommand(commandText, connect, timeOut);
        }
        private DataCommand(string commandText, DatabaseConnect connect, Int32? timeOut)
        {
            this._Connect = connect;
            SqlConnection connection = connect.Connector;
            SqlTransaction transaction = connect.Transaction;
            this._SqlCommand = (transaction != null ? new SqlCommand(commandText, connection, transaction) : new SqlCommand(commandText, connection));
            this._SqlCommand.StatementCompleted +=new StatementCompletedEventHandler(_SqlCommand_StatementCompleted);
            if (timeOut.HasValue)
                this._SqlCommand.CommandTimeout = timeOut.Value;
        }
        void _SqlCommand_StatementCompleted(object sender, StatementCompletedEventArgs e)
        {
            this._Connect.CommandStatementCompleted(this, e);
        }
        private DatabaseConnect _Connect;
        private SqlCommand _SqlCommand;
        #endregion
        #region Bridge to SqlCommand
        /// <summary>
        /// Gets or sets the Transact-SQL statement, table name or stored procedure to execute at the data source.
        /// </summary>
        public string CommandText { get { return this._SqlCommand.CommandText; } set { this._SqlCommand.CommandText = value; } }
        /// <summary>
        /// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.
        /// The time in seconds to wait for the command to execute. The default is 30 seconds.
        /// </summary>
        public int CommandTimeout { get { return this._SqlCommand.CommandTimeout; } set { this._SqlCommand.CommandTimeout = value; } }
        /// <summary>
        /// Gets or sets a value indicating how the System.Data.SqlClient.SqlCommand.CommandText property is to be interpreted.
        /// </summary>
        public CommandType CommandType { get { return this._SqlCommand.CommandType; } set { this._SqlCommand.CommandType = value; } }
        /// <summary>
        /// Gets the System.Data.SqlClient.SqlConnection used by this instance of the System.Data.SqlClient.SqlCommand.
        /// </summary>
        public SqlConnection Connection { get { return this._SqlCommand.Connection; } }
        /// <summary>
        /// Gets the System.Data.SqlClient.SqlParameterCollection.
        /// </summary>
        public SqlParameterCollection Parameters  { get { return this._SqlCommand.Parameters; } }
        /// <summary>
        /// Gets the System.Data.SqlClient.SqlTransaction within which the System.Data.SqlClient.SqlCommand executes.
        /// </summary>
        public SqlTransaction Transaction { get { return this._SqlCommand.Transaction; } }
        /// <summary>
        /// Initiates the asynchronous execution of the Transact-SQL statement or stored procedure that is described by this System.Data.SqlClient.SqlCommand.
        /// </summary>
        /// <returns>
        /// An System.IAsyncResult that can be used to poll or wait for results, or both;
        /// this value is also needed when invoking System.Data.SqlClient.SqlCommand.EndExecuteNonQuery(System.IAsyncResult),
        /// which returns the number of affected rows.
        /// </returns>
        public IAsyncResult BeginExecuteNonQuery() { return this._SqlCommand.BeginExecuteNonQuery(); }
        /// <summary>
        ///     Initiates the asynchronous execution of the Transact-SQL statement or stored
        ///     procedure that is described by this System.Data.SqlClient.SqlCommand, given
        ///     a callback procedure and state information.
        /// </summary>
        /// <param name="callback">An System.AsyncCallback delegate that is invoked when the command's execution
        ///     has completed. Pass null (Nothing in Microsoft Visual Basic) to indicate
        ///     that no callback is required.</param>
        /// <param name="stateObject">A user-defined state object that is passed to the callback procedure. Retrieve
        ///     this object from within the callback procedure using the System.IAsyncResult.AsyncState
        ///     property.</param>
        /// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;
        ///     this value is also needed when invoking System.Data.SqlClient.SqlCommand.EndExecuteNonQuery(System.IAsyncResult),
        ///     which returns the number of affected rows.</returns>
        public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { return this._SqlCommand.BeginExecuteNonQuery(callback, stateObject); }
        /// <summary>
        /// Initiates the asynchronous execution of the Transact-SQL statement or stored
        ///     procedure that is described by this System.Data.SqlClient.SqlCommand, and
        ///     retrieves one or more result sets from the server.
        /// </summary>
        /// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;
        ///     this value is also needed when invoking System.Data.SqlClient.SqlCommand.EndExecuteReader(System.IAsyncResult),
        ///     which returns a System.Data.SqlClient.SqlDataReader instance that can be
        ///     used to retrieve the returned rows.</returns>
        public IAsyncResult BeginExecuteReader() { return this._SqlCommand.BeginExecuteReader(); }
        /// <summary>
        /// Initiates the asynchronous execution of the Transact-SQL statement or stored
        ///     procedure that is described by this System.Data.SqlClient.SqlCommand using
        ///     one of the System.Data.CommandBehavior values.
        /// </summary>
        /// <param name="behavior">One of the System.Data.CommandBehavior values, indicating options for statement
        ///     execution and data retrieval.</param>
        /// <returns>An System.IAsyncResult that can be used to poll, wait for results, or both;
        ///     this value is also needed when invoking System.Data.SqlClient.SqlCommand.EndExecuteReader(System.IAsyncResult),
        ///     which returns a System.Data.SqlClient.SqlDataReader instance that can be
        ///     used to retrieve the returned rows.</returns>
        public IAsyncResult BeginExecuteReader(CommandBehavior behavior) { return this._SqlCommand.BeginExecuteReader(behavior); }
        public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject) { return this._SqlCommand.BeginExecuteReader(callback, stateObject); }
        public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) { return this._SqlCommand.BeginExecuteReader(callback, stateObject, behavior); }
        /// <summary>
        /// Tries to cancel the execution of a System.Data.SqlClient.SqlCommand.
        /// </summary>
        public void Cancel() { this._SqlCommand.Cancel(); }
        /// <summary>
        /// Creates a new instance of a System.Data.SqlClient.SqlParameter object.
        /// </summary>
        /// <returns>A System.Data.SqlClient.SqlParameter object.</returns>
        public SqlParameter CreateParameter() { return this._SqlCommand.CreateParameter(); }
        public int EndExecuteNonQuery(IAsyncResult asyncResult) { return this._SqlCommand.EndExecuteNonQuery(asyncResult); }
        public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) { return this._SqlCommand.EndExecuteReader(asyncResult); }
        /// <summary>
        /// Executes a Transact-SQL statement against the connection and returns the number of rows affected.
        /// </summary>
        /// <returns>The number of rows affected.</returns>
        public int ExecuteNonQuery() { return this._SqlCommand.ExecuteNonQuery(); }
        /// <summary>
        ///  Sends the System.Data.SqlClient.SqlCommand.CommandText to the System.Data.SqlClient.SqlCommand.Connection and builds a System.Data.SqlClient.SqlDataReader.
        /// </summary>
        /// <returns> A System.Data.SqlClient.SqlDataReader object.</returns>
        public SqlDataReader ExecuteReader() { return this._SqlCommand.ExecuteReader(); }
        /// <summary>
        /// Sends the System.Data.SqlClient.SqlCommand.CommandText to the System.Data.SqlClient.SqlCommand.Connection,
        ///     and builds a System.Data.SqlClient.SqlDataReader using one of the System.Data.CommandBehavior
        ///     values.
        /// </summary>
        /// <param name="behavior">One of the System.Data.CommandBehavior values.</param>
        /// <returns>A System.Data.SqlClient.SqlDataReader object.</returns>
        public SqlDataReader ExecuteReader(CommandBehavior behavior) { return this._SqlCommand.ExecuteReader(behavior); }
        /// <summary>
        /// Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
        /// </summary>
        /// <returns>The first column of the first row in the result set, or a null reference if the result set is empty. Returns a maximum of 2033 characters.</returns>
        public object ExecuteScalar() { return this._SqlCommand.ExecuteScalar(); }
        #endregion
        #region IDisposable Members
        void IDisposable.Dispose()
        {
            this._Connect.CommandStatementCompleted(this, new StatementCompletedEventArgs(-1));
            this._SqlCommand.Dispose();
            this._SqlCommand = null;
            this._Connect = null;
        }
        #endregion
    }
    #endregion
    #region SqlFormat : zajišťuje konverzi formátů pro SQL příkazy
    /// <summary>
    /// SqlFormat : zajišťuje konverzi formátů pro SQL příkazy
    /// </summary>
    public static class SqlFormat
    {
        #region Konverzní metoda (any) to SQL string: ToSql(value)
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(bool value)
        {
            return " " + (value ? "1" : "0");
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlBoolean value)
        {
            return " " + (value.IsNull ? "NULL" : (value ? "1" : "0"));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(byte value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlByte value)
        {
            return " " + (value.IsNull ? "NULL" : value.Value.ToString("N", NumFmt));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(short value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(ushort value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlInt16 value)
        {
            return " " + (value.IsNull ? "NULL" : value.Value.ToString("N", NumFmt));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(int value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(uint value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlInt32 value)
        {
            return " " + (value.IsNull ? "NULL" : value.Value.ToString("N", NumFmt));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(long value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(ulong value)
        {
            return " " + value.ToString("N", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlInt64 value)
        {
            return " " + (value.IsNull ? "NULL" : value.Value.ToString("N", NumFmt));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(decimal value)
        {
            return " " + value.ToString("C", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlDecimal value)
        {
            return " " + (value.IsNull ? "NULL" : value.Value.ToString("C", NumFmt));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(float value)
        {
            return " " + value.ToString("C", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(double value)
        {
            return " " + value.ToString("C", NumFmt);
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlDouble value)
        {
            return " " + (value.IsNull ? "NULL" : value.Value.ToString("C", NumFmt));
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(string value)
        {
            if (value == null) return " NULL";
            return " N'" + value.Replace("'", "''") + "'";
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlString value)
        {
            if (value.IsNull) return " NULL";
            return " N'" + value.Value.Replace("'", "''") + "'";
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(DateTime value)
        {
            return " '" + value.ToString("yyyy-MM-dd HH:mm:ss") + "'";
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(SqlDateTime value)
        {
            return " " + (value.IsNull ? "NULL" : "'" + value.Value.ToString("yyyy-MM-dd HH:mm:ss") + "'");
        }
        /// <summary>
        /// Vrátí danou hodnotu naformátovanou pro SQL příkaz
        /// </summary>
        /// <param name="value">Hodnota</param>
        /// <returns>Formát do SQL příkazu</returns>
        public static string ToSql(object value)
        {
            if (value == null)
                return " NULL";

            string type = value.GetType().Name;
            switch (type)
            {
                case "Boolean":
                    return ToSql((bool)value);
                case "SqlBoolean":
                    return ToSql((SqlBoolean)value);
                case "byte":
                    return ToSql((byte)value);
                case "SqlByte":
                    return ToSql((SqlByte)value);
                case "Int16":
                    return ToSql((short)value);
                case "UInt16":
                    return ToSql((ushort)value);
                case "SqlInt16":
                    return ToSql((SqlInt16)value);
                case "Int32":
                    return ToSql((int)value);
                case "UInt32":
                    return ToSql((uint)value);
                case "SqlInt32":
                    return ToSql((SqlInt32)value);
                case "Int64":
                    return ToSql((long)value);
                case "UInt64":
                    return ToSql((ulong)value);
                case "SqlInt64":
                    return ToSql((SqlInt64)value);
                case "Decimal":
                    return ToSql((Decimal)value);
                case "SqlDecimal":
                    return ToSql((SqlDecimal)value);
                case "Single":
                case "float":
                    return ToSql((float)value);
                case "Double":
                    return ToSql((Double)value);
                case "SqlDouble":
                    return ToSql((SqlDouble)value);
                case "String":
                    return ToSql((string)value);
                case "SqlString":
                    return ToSql((SqlString)value);
                case "DateTime":
                    return ToSql((DateTime)value);
                case "SqlDateTime":
                    return ToSql((SqlDateTime)value);
            }
            return " " + value;
        }
        /// <summary>
        /// NumberFormatInfo pro formátování čísel, static autoinstanční
        /// </summary>
        private static System.Globalization.NumberFormatInfo NumFmt
        {
            get
            {
                if (_NumFmt == null)
                {
                    _NumFmt = new System.Globalization.NumberFormatInfo();
                    _NumFmt.CurrencyDecimalDigits = 6;
                    _NumFmt.CurrencyDecimalSeparator = ".";
                    _NumFmt.CurrencyGroupSeparator = "";
                    _NumFmt.CurrencyGroupSizes = new int[0];
                    _NumFmt.CurrencySymbol = "";
                    _NumFmt.CurrencyNegativePattern = 2;            // $-n přičemž % je CurrencySymbol, což je zadáno ""
                    _NumFmt.CurrencyPositivePattern = 0;            // $n  přičemž % je CurrencySymbol, což je zadáno ""

                    _NumFmt.NegativeSign = "-";
                    _NumFmt.NumberDecimalDigits = 0;
                    _NumFmt.NumberDecimalSeparator = ".";
                    _NumFmt.NumberGroupSeparator = "";
                    _NumFmt.PositiveSign = "";
                }
                return _NumFmt;
            }
        }
        private static System.Globalization.NumberFormatInfo _NumFmt;
        #endregion
        #region SetToken(ref sql, token, value)
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, short value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, int value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, long value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, decimal value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, float value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, string value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }
        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="value">Hodnota</param>
        public static void SetToken(ref string sql, string token, DateTime value)
        {
            _SetToken(ref sql, token, ToSql(value));
        }

        /// <summary>
        /// Do SQL příkazu na místo tokenu vloží konkrétní hodnotu, která je již připravená pro SQL příkaz
        /// </summary>
        /// <param name="sql">Ref příkaz SQL, může obsahovat tokeny</param>
        /// <param name="token">Text tokenu: tento text se vyhledá a nahradí zformátovanou hodnotou value</param>
        /// <param name="sqlValue">Hodnota</param>
        private static void _SetToken(ref string sql, string token, string sqlValue)
        {
            if (sql == null || token == null || sqlValue == null) return;
            if (sql.Contains(token))
                sql = sql.Replace(token, sqlValue);
        }
        #endregion
        #region Konverzní metoda (any) to SQL comment
        /// <summary>
        /// Daný text zabalí do znaků SQL komentáře:  /* text */.
        /// Pokud by se ale v textu vyskytoval string konce komentáře, bude odstraněn, aby se zabránilo injectu:
        /// Příklad textu: "Popisek */ ; exec dbproc ..." : by ukončil komentář, ukončil by SQL příkaz a spustil by něco jiného.
        /// Obrana: vrátí se takovýto výsledek (komentář): "/* Popisek * / ; exec dbproc ... */"
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        internal static string ToComment(string text)
        {
            if (text == null) return "";
            string result = text;
            while (result.Contains("*/"))
                result = result.Replace("*/", "* /");
            return "/* " + result + " */";
        }
        #endregion
    }
    #endregion
}
